package crmdna.programtype;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.objectify.ObjectifyFilter;

import crmdna.client.Client;
import crmdna.common.APIException;
import crmdna.common.APIResponse.Status;
import crmdna.practice.Practice;
import crmdna.practice.Practice.PracticeProp;
import crmdna.user.User;
import crmdna.user.User.Action;
import crmdna.user.User.ResourceType;

public class ProgramTypeTest {
	
	private final LocalServiceTestHelper datastoreHelper =
		        new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
		        .setApplyAllHighRepJobPolicy());
	
	private final String client = "isha";
 	private final String invalidClient = "invalid";
 	private final String validUser = "valid@login.com";
 	private final String userWithPermission = "withpermission@login.com";
	
    @Before
    public void setUp() {
        datastoreHelper.setUp();  
        ObjectifyFilter.complete();
        
        Client.create(client);
        
        //can call getAll without any records
    	List<ProgramTypeProp> props = ProgramType.getAll(client);
    	assertEquals(0, props.size());
    	
        long groupId = crmdna.group.Group.create(client, "Singapore", User.SUPER_USER).groupId;
        assertEquals(1, groupId);
       
        User.create(client, validUser, groupId, User.SUPER_USER);
        assertEquals(1, User.get(client, validUser).toProp().userId);
        
        User.create(client, userWithPermission, groupId, User.SUPER_USER);
        assertEquals(2, User.get(client, userWithPermission).toProp().userId);
        
        User.addOrDeletePrivilege(client, userWithPermission, ResourceType.PROGRAM_TYPE, 0, 
        		Action.WRITE, true, User.SUPER_USER);             
    }

    @After
    public void tearDown() {       
    	ObjectifyFilter.complete();
        datastoreHelper.tearDown();
    }
    
    @Test
    public void createTest() {    	
    	
    	long ishaKriya = Practice.create(client, "Isha Kriya", User.SUPER_USER).practiceId;
    	Set<Long> practiceIds = new HashSet<>();
    	practiceIds.add(ishaKriya);
    	ProgramTypeProp prop = ProgramType.create(client, "Isha Kriya Online", 
    			practiceIds, userWithPermission);
    	assertEquals("first entity has id 1", 1, prop.programTypeId);
    	
    	prop = ProgramType.safeGet(client, prop.programTypeId).toProp();
    	assertEquals("name is populated correctly", "isha kriya online", prop.name);
    	assertEquals("display name is populated correctly", 
    			"Isha Kriya Online", prop.displayName);
    	assertEquals(1, prop.practiceProps.size());
    	assertEquals("isha kriya", prop.practiceProps.get(0).name);
    	
    	//cannot create duplicate
    	try {
    		ProgramType.create("isha", "ISHA KRIYA onLine", practiceIds, User.SUPER_USER);
    		assertTrue(false);
    	} catch (APIException e) {
    		assertEquals("cannot create duplicate", 
    				Status.ERROR_RESOURCE_ALREADY_EXISTS, e.statusCode);
    	}
    	
    	    	
    	//ids should be in sequence
    	prop = ProgramType.create(client, "Shambhavi 2 day", practiceIds, userWithPermission);
    	assertEquals("id is in sequence", 2, prop.programTypeId);
    	prop = ProgramType.create(client, "Inner Engineering Retreat", practiceIds, userWithPermission);
    	assertEquals("practice id is in sequence", 3, prop.programTypeId);
    	prop = ProgramType.create(client, "Surya Kriya", practiceIds, userWithPermission);
    	assertEquals("practice id is in sequence", 4, prop.programTypeId);
    	
    	prop = ProgramType.safeGet(client, 2).toProp();
    	assertEquals("name is populated correctly", "shambhavi 2 day", prop.name);
    	assertEquals("display name is populated correctly", "Shambhavi 2 day", prop.displayName);
    	prop = ProgramType.safeGet(client, 3).toProp();
    	assertEquals("display name is populated correctly", 
    			"Inner Engineering Retreat", prop.displayName);
    	prop = ProgramType.safeGet(client, 4).toProp();
    	assertEquals("display name is populated correctly", "Surya Kriya", prop.displayName);
    	assertEquals("name is populated correctly", "surya kriya", prop.name);
    	
    	//access control
    	try {
    		ProgramType.create("isha", "Hata Yoga - Asanas", practiceIds, validUser);
    		assertTrue(false);
    	} catch (APIException e) {
    		assertEquals("permission required to edit program type", 
    				Status.ERROR_INSUFFICIENT_PERMISSION, e.statusCode);
    	}
    	
    	//client should be valid
    	try {
    		ProgramType.create(invalidClient, "Hata Yoga - Upa Yoga", practiceIds, User.SUPER_USER);
    		assertTrue(false);
    	} catch (APIException e) {
    		assertEquals("client should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, e.statusCode);
    	}    	
    }
    
    @Test
    public void safeGetTest() {   
    	long shoonya = Practice.create(client, "Shoonya", User.SUPER_USER).practiceId;
    	long shakthiChalana = Practice.create(client, "Shakthi Chalana Kriya", User.SUPER_USER).practiceId;
    	long suryaNamaskar = Practice.create(client, "Surya Namaskar", User.SUPER_USER).practiceId;
    	
    	Set<Long> practiceIds = new HashSet<>();
    	practiceIds.add(suryaNamaskar);
    	practiceIds.add(shoonya);
    	practiceIds.add(shakthiChalana);
    	
    	ProgramTypeProp prop = ProgramType.create(client, "Shoonya Intensive", practiceIds, userWithPermission);
    	assertTrue(prop.programTypeId != 0);
    	
    	prop = ProgramType.safeGet("isha", prop.programTypeId).toProp();
    	assertEquals("name is populated correctly", "shoonya intensive", prop.name);
    	assertEquals("display name is populated correctly", "Shoonya Intensive", prop.displayName);
    	
    	//practice should be sorted by name
    	assertEquals("shakthi chalana kriya", prop.practiceProps.get(0).name);
    	assertEquals("shoonya", prop.practiceProps.get(1).name);
    	assertEquals("surya namaskar", prop.practiceProps.get(2).name);
    	
    	//exception for non existing
    	try {
    		ProgramType.safeGet("isha", prop.programTypeId + 20939); //non existant
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals("exception thrown for non exisitng practise id", 
    				Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    }
    
    @Test public void ensureValidTest() {
    	Set<Long> programTypeIds = new HashSet<>();
    	programTypeIds.add((long)100);
    	programTypeIds.add((long)101);
    	
    	try {
    		ProgramType.ensureValid(client, programTypeIds);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	
    	//add some program types
    	ProgramTypeProp pt1 = ProgramType.create(client, "PT1", null, User.SUPER_USER);
    	ProgramTypeProp pt2 = ProgramType.create(client, "PT2", null, User.SUPER_USER);
    	
    	try {
    		ProgramType.ensureValid(client, programTypeIds);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	
    	programTypeIds.clear();
    	programTypeIds.add(pt1.programTypeId);
    	programTypeIds.add(pt2.programTypeId);
    	
    	ProgramType.ensureValid(client, programTypeIds); //no exception
    }
    
    @Test
    public void renameTest() {  
    	long shoonya = Practice.create(client, "Shoonya", User.SUPER_USER).practiceId;
    	long shakthiChalana = Practice.create(client, "Shakthi Chalana Kriya", User.SUPER_USER).practiceId;
    	long suryaNamaskar = Practice.create(client, "Surya Namaskar", User.SUPER_USER).practiceId;
    	long shambhavi = Practice.create(client, "Shambhavi", User.SUPER_USER).practiceId;
    	
    	Set<Long> practiceIds = new HashSet<>();
    	practiceIds.add(suryaNamaskar);
    	practiceIds.add(shoonya);
    	practiceIds.add(shakthiChalana);
    	
    	ProgramTypeProp prop = ProgramType.create("isha", "Shoonya Intensive", 
    			practiceIds, userWithPermission);
    	practiceIds.clear();
    	practiceIds.add(shambhavi);
    	ProgramType.create(client, "Shambhavi 2 day", practiceIds, userWithPermission);
    	
    	try {
    		ProgramType.rename("isha", prop.programTypeId, "Shambhavi 2 day", userWithPermission);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals("cannot rename to existing", 
    				Status.ERROR_RESOURCE_ALREADY_EXISTS, ex.statusCode);
    	}
    	
    	//can change case and rename
    	ProgramType.rename("isha", prop.programTypeId, "shoonya intensive", userWithPermission);
    	prop = ProgramType.safeGet("isha", prop.programTypeId).toProp();
    	assertEquals("name correctly populated after rename", "shoonya intensive", prop.name);
    	assertEquals("name correctly populated after rename", "shoonya intensive", prop.displayName);
    	
    	ProgramType.rename("isha", prop.programTypeId, "Shoonya Meditation", userWithPermission);
    	prop = ProgramType.safeGet("isha", prop.programTypeId).toProp();
    	assertEquals("name populated correctly", "shoonya meditation", prop.name);
    	assertEquals("display name populated correctly", "Shoonya Meditation", prop.displayName);    
    	
    	try {
    		ProgramType.rename("isha", prop.programTypeId, "Shoonya Training", validUser);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals("permission required to rename practice", 
    				Status.ERROR_INSUFFICIENT_PERMISSION, ex.statusCode);
    	}    
    	
    	try {
    		ProgramType.rename(invalidClient, prop.programTypeId, "Shoonya", userWithPermission);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals("client should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}      	
    }
    
    @Test
    public void getAllTest() {    	    	    
    	
    	try {
    		ProgramType.getAll(invalidClient);
    	} catch (APIException ex) {
    		assertEquals("client should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	
    	assertEquals("getall returns empty list if no records", 0, ProgramType.getAll(client).size());
    	
    	long suryaNamaskar = Practice.create(client, "Surya Namaskar", User.SUPER_USER).practiceId;
    	long shoonya = Practice.create(client, "Shoonya", User.SUPER_USER).practiceId;
    	long shakthiChalana = Practice.create(client, "Shakthi Chalana Kriya", User.SUPER_USER).practiceId;
    	long shambhavi = Practice.create(client, "Shambhavi", User.SUPER_USER).practiceId;
    	
    	Set<Long> practiceIds = new HashSet<>();
    	practiceIds.add(suryaNamaskar);
    	practiceIds.add(shoonya);
    	practiceIds.add(shakthiChalana);
    	ProgramType.create(client, "Shoonya Intensive", practiceIds, userWithPermission);
    	
    	practiceIds.clear();
    	practiceIds.add(shambhavi);
    	ProgramType.create(client, "Shambhavi 2 day", practiceIds, userWithPermission);    	
    	ObjectifyFilter.complete();
    	
    	List<ProgramTypeProp> props = ProgramType.getAll(client);
    	assertEquals(2, props.size());
    	//should be sorted
    	assertEquals("shambhavi 2 day", props.get(0).name);
    	assertEquals("Shambhavi 2 day", props.get(0).displayName);
    	assertEquals(1, props.get(0).practiceProps.size());
    	assertEquals("shambhavi", ((PracticeProp)props.get(0).practiceProps.toArray()[0]).name);
    	
    	assertEquals("shoonya intensive", props.get(1).name);
    	assertEquals("Shoonya Intensive", props.get(1).displayName); 
    	assertEquals(3, props.get(1).practiceProps.size());
    	//practice should be sorted by name
    	assertEquals("shakthi chalana kriya", props.get(1).practiceProps.get(0).name);
    	assertEquals("shoonya", props.get(1).practiceProps.get(1).name);
    	assertEquals("surya namaskar", props.get(1).practiceProps.get(2).name);
    }  
    
    @Test
    public void getPracticeIdsTest() {
    	PracticeProp p1 = Practice.create(client, "p1", User.SUPER_USER);
    	PracticeProp p2 = Practice.create(client, "p2", User.SUPER_USER);
    	PracticeProp p3 = Practice.create(client, "p3", User.SUPER_USER);
    	
    	Set<Long> practiceIds = new HashSet<>();
    	practiceIds.add(p1.practiceId);
    	practiceIds.add(p2.practiceId);
    	ProgramTypeProp pt1 = ProgramType.create(client, "pt1", practiceIds, User.SUPER_USER);
    	
    	practiceIds.clear();
    	practiceIds.add(p2.practiceId);
    	practiceIds.add(p3.practiceId);
    	ProgramTypeProp pt2 = ProgramType.create(client, "pt2", practiceIds, User.SUPER_USER);
    	
    	Set<Long> programTypeIds = new HashSet<>();
    	programTypeIds.add(pt1.programTypeId);    	
    	programTypeIds.add((long) 100);
    	
    	practiceIds = ProgramType.getPracticeIds(client, programTypeIds);
    	assertEquals(2, practiceIds.size());
    	assertTrue(practiceIds.contains(p1.practiceId));
    	assertTrue(practiceIds.contains(p2.practiceId));
    	
    	programTypeIds.add(pt2.programTypeId);
    	practiceIds = ProgramType.getPracticeIds(client, programTypeIds);
    	assertEquals(3, practiceIds.size());
    	assertTrue(practiceIds.contains(p1.practiceId));
    	assertTrue(practiceIds.contains(p2.practiceId));
    	assertTrue(practiceIds.contains(p3.practiceId));
    }
    
    @Test
    public void toPropTest() {
    	ProgramTypeEntity entity = new ProgramTypeEntity();
    	entity.programTypeId = 123l;
    	entity.displayName = "Shoonya Intensive";
    	entity.name = entity.displayName.toLowerCase();
    	
    	ProgramTypeProp prop = entity.toProp();
    	assertEquals("practise id correctly populated", 123, prop.programTypeId);
    	assertEquals("display name correctly populated", "Shoonya Intensive", prop.displayName);
    	assertEquals("name correctly populated", "shoonya intensive", prop.name);    	    	
    }        
}
